对非类型模板参数的限制中，可能最让初学者和高级模板编写者感到惊讶的是不能提供字符串字面值作为模板参数。

看看下面的例子：

\begin{lstlisting}[style=styleCXX]
template<char const* msg>
class Diagnoser {
	public:
	void print();
};

int main()
{
	Diagnoser<"Surprise!">().print();
}
\end{lstlisting}

然而，其中有一些问题。标准C++中，当两个Diagnostic实例具有相同的参数时，其类型相同。参数是一个指针值，是一个地址。但出现在不同源位置的两个相同字符串，并不要求具有相同的地址。可能会尴尬的发现，Diagnoser<"X">和Diagnoser<"X">实际上是两种不同且不兼容的类型!(请注意，"X"的类型是char const[2]，但当作为模板参数传递时，会衰变为char const*)

由于这些(以及相关的)情况，C++标准禁止将字符串字面值作为模板的参数。然而，一些实现确实将该工具作为扩展提供。通过在模板实例的内部表示中使用实际的字符串文字内容来实现这一点。尽管是可行的，但一些C++语言评论者认为，可以用字符串字面值替换的非类型模板参数，声明方式应该与可以用地址替换的非类型模板参数不同。一种可能是在字符参数包中捕获字符串字面值，例如:

\begin{lstlisting}[style=styleCXX]
template<char... msg>
class Diagnoser {
	public:
	void print();
};

int main()
{
	// instantiates Diagnoser<’S’,’u’,’r’,’p’,’r’,’i’,’s’,’e’,’!’>
	Diagnoser<"Surprise!">().print();
}
\end{lstlisting}

还应该注意到另一个技术问题。考虑下面的模板声明，假设语言已经扩展为接受字符串字面值作为模板参数:

\begin{lstlisting}[style=styleCXX]
template<char const* str>
class Bracket {
	public:
	static char const* address();
	static char const* bytes();
};

template<char const* str>
char const* Bracket<str>::address()
{
	return str;
}

template<char const* str>
char const* Bracket<str>::bytes()
{
	return str;
}
\end{lstlisting}

前面的代码中，除了名称之外，两个成员函数是相同的——这种情况很常见。想象一下，实现使用类似于宏展开的方式实例化Bracket<"X">:若两个成员函数在不同的翻译单元中实例化，可能返回不同的值。有趣的是，对目前提供此扩展的一些C++编译器的测试表明，确实受到了这种行为的影响。

相关的问题是提供浮点字面量(和简单的浮点常量表达式)作为模板参数的能力。例如:

\begin{lstlisting}[style=styleCXX]
template<double Ratio>
class Converter {
	public:
	static double convert (double val) {
		return val*Ratio;
	}
};

using InchToMeter = Converter<0.0254>;
\end{lstlisting}

这也由一些C++实现提供，并且不存在严重的技术挑战(不像字符串字面量参数)。

C++11引入了文字类类型的概念:这种类类型可以在编译时计算的常量值(包括通过constexpr函数进行的复杂计算)。当这种类类型可用，就需要将其用于非类型模板参数。但出现了与上面描述的字符串字面量参数类似的问题。两个类类型值的“相等”不简单，因为通常是由operator==确定。这种相等性决定两个实例化是否等价，但在实践中，链接器必须通过比较损坏的名称来检查这种等价性。解决方法可能是选择将某些文字类标记为具有简单的相等标准，相当于对类的标量成员进行比较。只有具有这种简单的相等条件的类类型，才允许作为非类型模板参数类型。




































